#include "Controller.h"
#include "Voice.h"


Controller::Controller (Ptr< list< Ptr< Object > > > pObjList,
                        Ptr< BattleStatus > pBattleStatus,
                        Ptr< queue< Request > > pReqQueue,
                        Ptr< Terrain > pTerrain)
{

  _pObjList = pObjList;
  _pBattleStatus = pBattleStatus;
  _pReqQueue = pReqQueue;
  _pTerrain = pTerrain;
  init_property_list ();

}

void Controller::run ()
{
  printf("Controller Here\n");
  while (true)
  {
    // Handle user's request if exists
    if (_pReqQueue->size ())
    {
      // Get request from the head of queue
      Request request = _pReqQueue->front ();
      _pReqQueue->pop();

      switch (request.requestType)
      {
      case Request::Move:
        move_current_to (request.x, request.y); break;
      case Request::Create:
        create_obj_at (request.type, request.x, request.y); break;
      case Request::Select:
        select_at (request.x, request.y); break;
      case Request::Delete:
        delete_selected (); break;
      case Request::Fight:
        attack_toward (request.x, request.y); break;
      case Request::End:
        end_this_turn(); break;
      default: break;
      } // End switch

    } // End if

    // Call all objects to do their actions automatically
    list< Ptr< Object > >::iterator iter = _pObjList->begin();
    for (; iter != _pObjList->end(); iter++)
    {
      if ((*iter)->update () == false) // the object has died
      {
        iter = _pObjList->erase (iter);
		if (iter == _pObjList->end ())
			break;
      }
    }

    // Rescan the battle to see if there are died objects to remove
    iter = _pObjList->begin();
    for (; iter != _pObjList->end(); ++iter)
    {
      if (!(*iter)->exist ())
      {
        iter = _pObjList->erase (iter);
		if (iter == _pObjList->end ())
			break;      }
    }

    // Time escapes and see whether time is out
    if (_pBattleStatus->time_minus ())
      end_this_turn ();
	
	Sleep(2);
  } // End while

} // End run


// ========================================================================
// Internal Handle Functions
// ========================================================================

vector< Ptr< Object > > Controller::get_objs (float x , float y)
{

  vector< Ptr< Object > > objs;
  vector< float > distances;

	list< Ptr< Object > >::iterator iter = _pObjList->begin();

	for (; iter != _pObjList->end(); iter++) // Find all which are in reach
    {
		float dx = x - (*iter)->center ()[0];
		float dy = y - (*iter)->center ()[1];
        float square_of_distance = dx * dx + dy * dy;
        float r = (*iter)->properties ()->radius ();

		if(square_of_distance > r * r) // Too far
          continue;

        vector< Ptr< Object > >::iterator objs_iter = objs.begin ();
        vector< float >::iterator distances_iter = distances.begin ();

		bool haveInserted = false;
		// Insert and Sort by distance increasingly
		for (; objs_iter != objs.end (); ++objs_iter, ++distances_iter)
		{
		  if ((*distances_iter) > square_of_distance)
		  {
			objs.insert (objs_iter, *iter);
			distances.insert (distances_iter, square_of_distance);
			haveInserted = true;
			break;
		  }
		}
		if (!haveInserted)
		{
		  objs.push_back (*iter);
		  distances.push_back (square_of_distance);
		}
	}

	return objs;

} // End get_objs


bool Controller::select_object (Ptr< Object > target)
{

  if (!target->properties ()->selectable ())
    return false;

  if (currentSelected != NULL)
    currentSelected->deselect ();

  currentSelected = target;

  currentSelected->select ();

  if(_pBattleStatus->cur_player().id == PLAYER_RED)
	  PlaySound(Team1_Select);
  else if(_pBattleStatus->cur_player().id == PLAYER_GREEN)
	  PlaySound(Team2_Select);

  return true;

} // End select_object


void Controller::delete_obj (Ptr< Object >& obj)
{

  _pObjList->remove (obj);
  obj = NULL;
  
} // End delete_obj


// ========================================================================
// User Request Handler
// ========================================================================


void Controller::create_obj_at (UINT objType, float x, float y)
{
	
  Ptr< Object > newObj = new Object (objType, _pBattleStatus->cur_player (),
    _pPropertyList[objType], _pTerrain, x, y);

  _pObjList->push_back (newObj);

  select_object (newObj);
} // End create_obj_at


void Controller::delete_selected ()
{

  if (currentSelected != NULL)
  {
    delete_obj (currentSelected);
  }

} // End delete_selected


void Controller::select_at (float x, float y)
{
	vector< Ptr< Object > > objs = get_objs (x, y);
  if (objs.size () == 0) return;
  for (UINT i = 0; i < objs.size (); ++i)
  {
    if (objs[i]->owner () != _pBattleStatus->cur_player ()) // Not belongs to the player
      continue;
    if (objs[i]->properties ()->selectable () == false) // Can't be selected
      continue;

    select_object (objs[i]);
    break;
  }
  return;
} // End select_at


void Controller::move_current_to (float x, float y)
{
	if (currentSelected == NULL)
		return;
	if (x == -1 || y == -1)
		return;
	currentSelected->set_target (x, y);
	
  if(_pBattleStatus->cur_player().id == PLAYER_RED)
	  PlaySound(Team1_Move);
  else if(_pBattleStatus->cur_player().id == PLAYER_GREEN)
	  PlaySound(Team2_Move);

} // End move_current_to


void Controller::attack_toward (float x, float y)
{
	if (currentSelected == NULL)
		return;
  vector< Ptr< Object > > objs = get_objs (x, y);
  for (UINT i = 0; i < objs.size (); ++i)
  {
    if (objs[i]->owner () != _pBattleStatus->cur_player ())
    {
      currentSelected->set_target (objs[i]);
      return;
    }
  }

  
  if(_pBattleStatus->cur_player().id == PLAYER_RED)
	  PlaySound(Team1_Attack);
  else if(_pBattleStatus->cur_player().id == PLAYER_GREEN)
	  PlaySound(Team2_Attack);

} // End attack_toward


void Controller::end_this_turn ()
{

  _pBattleStatus->shift ();

  while (!_pReqQueue->empty())
    _pReqQueue->pop();

  if (currentSelected != NULL)
  {
	  currentSelected->deselect ();
	  currentSelected = NULL;
  }
} // End end_this_turn



void Controller::init_property_list ()
{
  Ptr< ObjectProperty > pArmy = new ObjectProperty (2.5f, true, true, true,
    0.3f, // basic attack power
    3.0f, // basic attack range
    0.1f, // basic missing chance
    0.1f, // basic defense
    0.1f, // basic avoiding chance
    0.01f  // basic speed
    );
  _pPropertyList.push_back (pArmy);
}